import base64
import hashlib
import hmac
import json
import logging
import os
import re
import time
import urllib.parse

import colorlog
from logging.handlers import RotatingFileHandler
import datetime
import requests
from flask import g
from werkzeug.local import Local

# 配置日志文件名称及路径

cur_path = os.path.dirname(os.path.realpath(__file__))  # 当前项目路径
log_path = os.path.join(cur_path, '../../logs')  # log_path为存放日志的路径
if not os.path.exists(log_path):
    os.mkdir(log_path)  # 若不存在logs文件夹，则自动创建

log_colors_config = {
    # 终端输出日志颜色配置
    'DEBUG': 'white',
    'INFO': 'cyan',
    'WARNING': 'yellow',
    'ERROR': 'red',
    'CRITICAL': 'bold_red',
}

default_formats = {
    # 终端输出格式
    'color_format': '%(log_color)s%(asctime)s -%(levelname)s-: %(message)s',
    # 日志输出格式
    'log_format': '%(asctime)s - %(levelname)s : %(message)s'
}


class LogHandler:
    """
    先创建日志记录器（logging.getLogger），然后再设置日志级别（logger.setLevel），
    接着再创建日志文件，也就是日志保存的地方（logging.FileHandler），然后再设置日志格式（logging.Formatter），
    最后再将日志处理程序记录到记录器（addHandler）
    """

    def __init__(self, log_name):
        self.__now_time = datetime.datetime.now().strftime('%Y-%m-%d')  # 当前日期格式化

        # 收集所有日志文件，名称为：[日志名称] 2022-01-01_all.log；收集错误日志信息文件，名称为：[日志名称] 2022-01-01_error.log
        # 其中，[日志名称]为调用日志时的传入参数
        self.__all_log_path = os.path.join(log_path, log_name + "-" + self.__now_time + "-all" + ".log")  # 收集所有日志信息文件
        self.__error_log_path = os.path.join(log_path,
                                             log_name + "-" + self.__now_time + "-error" + ".log")  # 收集错误日志信息文件

        # 配置日志记录器及其级别 设置默认日志记录器记录级别为DEBUG
        self.__logger = logging.getLogger()  # 创建日志记录器
        self.__logger.setLevel(logging.DEBUG)  # 设置默认日志记录器记录级别

    @staticmethod
    def __init_logger_handler(log_path):
        """
        创建日志记录器handler，用于收集日志
        :param log_path: 日志文件路径
        :return: 日志记录器
        """
        # 写入文件，如果文件超过1M大小时，切割日志文件
        logger_handler = RotatingFileHandler(filename=log_path, maxBytes=1 * 1024 * 1024,
                                             encoding='utf-8')  # 可以设置 backupCount=3 在切割日志文件后仅保留3个文件
        return logger_handler

    @staticmethod
    def __init_console_handle():
        """创建终端日志记录器handler，用于输出到控制台"""
        console_handle = colorlog.StreamHandler()
        return console_handle

    def __set_log_handler(self, logger_handler, level=logging.DEBUG):
        """
        设置handler级别并添加到logger收集器
        :param logger_handler: 日志记录器
        :param level: 日志记录器级别
        """
        logger_handler.setLevel(level=level)
        self.__logger.addHandler(logger_handler)  # 添加到logger收集器

    def __set_color_handle(self, console_handle):
        """
        设置handler级别并添加到终端logger收集器
        :param console_handle: 终端日志记录器
        """
        console_handle.setLevel(logging.DEBUG)
        self.__logger.addHandler(console_handle)

    @staticmethod
    def __set_color_formatter(console_handle, color_config):
        """
        设置输出格式-控制台
        :param console_handle: 终端日志记录器
        :param color_config: 控制台打印颜色配置信息
        """
        formatter = colorlog.ColoredFormatter(default_formats["color_format"], log_colors=color_config)
        console_handle.setFormatter(formatter)

    @staticmethod
    def __set_log_formatter(file_handler):
        """
        设置日志输出格式-日志文件
        :param file_handler: 日志记录器
        """
        formatter = logging.Formatter(default_formats["log_format"],
                                      datefmt='')  # datefmt用于设置asctime的格式，例如：%a, %d %b %Y %H:%M:%S 或者 %Y-%m-%d %H:%M:%S
        file_handler.setFormatter(formatter)

    @staticmethod
    def __close_handler(file_handler):
        """
        关闭handler
        :param file_handler: 日志记录器
        """
        file_handler.close()

    def __console(self, level, message):
        """构造日志收集器"""
        # 创建日志文件
        all_logger_handler = self.__init_logger_handler(self.__all_log_path)  # 收集所有日志文件
        error_logger_handler = self.__init_logger_handler(self.__error_log_path)  # 收集错误日志信息文件
        # console_handle = self.__init_console_handle()

        # 设置日志文件格式
        self.__set_log_formatter(all_logger_handler)
        self.__set_log_formatter(error_logger_handler)
        # self.__set_color_formatter(console_handle, log_colors_config)

        self.__set_log_handler(all_logger_handler)  # 设置handler级别并添加到logger收集器
        self.__set_log_handler(error_logger_handler, level=logging.ERROR)
        # self.__set_color_handle(console_handle)

        if level == 'info':
            self.__logger.info(message)
        elif level == 'debug':
            self.__logger.debug(message)
        elif level == 'warning':
            self.__logger.warning(message)
        elif level == 'error':
            self.__logger.error(message)
        elif level == 'critical':
            self.__logger.critical(message)

        self.__logger.removeHandler(all_logger_handler)  # 避免日志输出重复问题
        self.__logger.removeHandler(error_logger_handler)
        # self.__logger.removeHandler(console_handle)

        self.__close_handler(all_logger_handler)  # 关闭handler
        self.__close_handler(error_logger_handler)

    def debug(self, message):
        self.__console('debug', message)

    def info(self, message):
        self.__console('info', message)

    def warning(self, message):
        self.__console('warning', message)

    def error(self, message):
        self.__console('error', message)

    def critical(self, message):
        self.__console('critical', message)


class DingMsgHandler:
    def __init__(self):
        self.SECRET = 'SEC7ac33b136a0b7fa82c4d05418558ec56013fb7331ad41d911801a0ee7ccba35a'
        self.ACCESS_TOKEN = "4a6e2df3462c382de65975cbda35f61eb5ddd09d6c71e0ab112cda3b692551cd"

    def get_timestamp_and_sign_by_secret(self, secret):
        """获取当前时间戳和密钥"""
        timestamp = str(round(time.time() * 1000))
        secret_enc = secret.encode('utf-8')
        string_to_sign = '{}\n{}'.format(timestamp, secret)
        string_to_sign_enc = string_to_sign.encode('utf-8')
        hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
        sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
        return timestamp, sign

    def send_error_msg_to_dingtalk_rebot(self, request, e):
        """向钉钉机器人发送错误消息"""
        from config import server_config
        data = None
        try:
            headers = {'Content-Type': 'application/json'}  # 定义数据类型
            timestamp, sign = self.get_timestamp_and_sign_by_secret(self.SECRET)
            webhook = f"https://oapi.dingtalk.com/robot/send?access_token={self.ACCESS_TOKEN}&timestamp={timestamp}&sign={sign}"
            # 定义要发送的数据
            method = str(request.method)
            url = f"{server_config.web_addr_pub}:{server_config.web_port} {str(request.full_path)}"
            request_data = {
                "json": None if request.method.upper() == "GET" else request.get_json(),
                "args": request.args.__dict__,
                "form": request.form.__dict__,
                "method": request.method,
                "token": request.cookies.get("token")
            }
            now_time = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")
            data = {
                "msgtype": "markdown",
                "markdown": {
                    "title": "项目出错啦",
                    "text": f"""#### 项目出错啦  \n >**请求URL**:  \n{method} {url}  \n >**请求体数据**:  \n{request_data}  \n >**错误信息**:  \n{str(e)}  \n >**时间**:  \n{now_time}\n"""
                }
            }
            res = requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求
        except Exception as e:
            print("发送钉钉机器人错误消息报错", str(e), data)

    def send_simple_msg_to_dingtalk_rebot(self, msg):
        """向钉钉机器人发送简单消息"""
        from config import server_config
        try:

            url = f"{server_config.web_addr_pub}:{server_config.web_port_pub}"
            now_time = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")
            data = {
                "msgtype": "markdown",
                "markdown": {
                    "title": "项目出错啦",
                    "text": f"""#### 服务异常  \n >**错误信息**: \n >**请求URL**: {url}  \n{str(msg)}  \n >**时间**:  \n{now_time}\n"""
                }
            }

            headers = {'Content-Type': 'application/json'}  # 定义数据类型
            timestamp, sign = self.get_timestamp_and_sign_by_secret(self.SECRET)
            webhook = f"https://oapi.dingtalk.com/robot/send?access_token={self.ACCESS_TOKEN}&timestamp={timestamp}&sign={sign}"

            res = requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求
        except Exception as e:
            print("发送钉钉机器人错误消息报错", str(e))

    def notify_dingtalk_msg(self, params: dict):
        """向钉钉机器人发送消息"""
        from config import server_config
        try:
            data = {
                "msgtype": "markdown",
                "markdown": {
                    "title": params['title'],
                    "text": params['content']
                }
            }

            headers = {'Content-Type': 'application/json'}  # 定义数据类型
            token = params['token']
            secret = params['secret']
            timestamp, sign = self.get_timestamp_and_sign_by_secret(secret)
            webhook = f"https://oapi.dingtalk.com/robot/send?access_token={token}&timestamp={timestamp}&sign={sign}"
            resp = requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求
            if resp.status_code != 200:
                print(f'发送钉钉机器人消息报错:{resp.status_code}:{resp.json()}')
        except Exception as e:
            print("发送钉钉机器人消息报错", str(e))

    def notify_feishu_msg(self, params: dict):
        """向飞书机器人发送消息"""
        timestamp = str(round(time.time()))
        token = params['token']
        secret = params['secret']
        content = params['content']

        try:
            sign = self.gen_feishu_sign(timestamp, secret)
            data = {
                "timestamp": timestamp,
                "sign": sign,
                "msg_type": "text",
                "content": '{\"text\":\"' + content + '\"}'
            }

            headers = {'Content-Type': 'application/json'}  # 定义数据类型
            webhook = f"https://open.feishu.cn/open-apis/bot/v2/hook/{token}"
            resp = requests.post(webhook, data=json.dumps(data), headers=headers)  # 发送post请求
            resp_data = resp.json()
            if resp.status_code != 200:
                print(f'发送飞书机器人消息报错:{resp.status_code}:{resp.json()}')
            satus_code = resp_data.get('StatusCode')
            if satus_code != 0:
                print(f'发送飞书机器人消息报错:{resp_data}')
        except Exception as e:
            print("发送飞书机器人消息报错", str(e))

    def gen_feishu_sign(self, timestamp, secret) -> bytes:
        # 拼接timestamp和secret
        string_to_sign = '{}\n{}'.format(timestamp, secret)
        hmac_code = hmac.new(string_to_sign.encode("utf-8"), digestmod=hashlib.sha256).digest()
        # 对结果进行base64处理
        sign = base64.b64encode(hmac_code).decode('utf-8')

        return sign

    def fill_token_params(self, before_content: str, values: dict):
        found_strs = re.findall('{[a-z_][a-z0-9_]*}', before_content, re.S)
        if len(found_strs) == 0:
            return before_content
        for variant_str in found_strs:
            variant_name = variant_str[1:len(variant_str) - 1]
            variant_value = values.get(variant_name)
            if not variant_value:
                variant_value = None
            before_content = before_content.replace(variant_str, str(variant_value), -1)

        return before_content



error_send = DingMsgHandler()
log_api = LogHandler('api')
log_model = LogHandler('model')
